using System;

namespace ICSharpCode.TextEditor.Document
{
    public class FoldMarker : ISegment, IComparable
    {
        [CLSCompliant(false)]
        protected int offset = -1;

        [CLSCompliant(false)]
        protected int length = -1;

        private bool isFolded = false;

        private string foldText = "...";

        private FoldType foldType = FoldType.Unspecified;

        private IDocument document = null;

        private int startLine = -1;

        private int startColumn;

        private int endLine = -1;

        private int endColumn;

        public int Offset
        {
            get
            {
                return offset;
            }
            set
            {
                offset = value;
                startLine = -1;
                endLine = -1;
            }
        }

        public int Length
        {
            get
            {
                return length;
            }
            set
            {
                length = value;
                endLine = -1;
            }
        }

        public FoldType FoldType
        {
            get
            {
                return foldType;
            }
            set
            {
                foldType = value;
            }
        }

        public int StartLine
        {
            get
            {
                if (startLine < 0)
                {
                    GetPointForOffset(document, offset, out startLine, out startColumn);
                }
                return startLine;
            }
        }

        public int StartColumn
        {
            get
            {
                if (startLine < 0)
                {
                    GetPointForOffset(document, offset, out startLine, out startColumn);
                }
                return startColumn;
            }
        }

        public int EndLine
        {
            get
            {
                if (endLine < 0)
                {
                    GetPointForOffset(document, checked(offset + length), out endLine, out endColumn);
                }
                return endLine;
            }
        }

        public int EndColumn
        {
            get
            {
                if (endLine < 0)
                {
                    GetPointForOffset(document, checked(offset + length), out endLine, out endColumn);
                }
                return endColumn;
            }
        }

        public bool IsFolded
        {
            get
            {
                return isFolded;
            }
            set
            {
                isFolded = value;
            }
        }

        public string FoldText => foldText;

        public string InnerText => document.GetText(offset, length);

        public override string ToString()
        {
            return $"[FoldMarker: Offset = {offset}, Length = {length}]";
        }

        private static void GetPointForOffset(IDocument document, int offset, out int line, out int column)
        {
            checked
            {
                if (offset > document.TextLength)
                {
                    line = document.TotalNumberOfLines + 1;
                    column = 1;
                }
                else if (offset < 0)
                {
                    line = -1;
                    column = -1;
                }
                else
                {
                    line = document.GetLineNumberForOffset(offset);
                    column = offset - document.GetLineSegment(line).Offset;
                }
            }
        }

        public FoldMarker(IDocument document, int offset, int length, string foldText, bool isFolded)
        {
            this.document = document;
            this.offset = offset;
            this.length = length;
            this.foldText = foldText;
            this.isFolded = isFolded;
        }

        public FoldMarker(IDocument document, int startLine, int startColumn, int endLine, int endColumn)
            : this(document, startLine, startColumn, endLine, endColumn, FoldType.Unspecified)
        {
        }

        public FoldMarker(IDocument document, int startLine, int startColumn, int endLine, int endColumn, FoldType foldType)
            : this(document, startLine, startColumn, endLine, endColumn, foldType, "...")
        {
        }

        public FoldMarker(IDocument document, int startLine, int startColumn, int endLine, int endColumn, FoldType foldType, string foldText)
            : this(document, startLine, startColumn, endLine, endColumn, foldType, foldText, isFolded: false)
        {
        }

        public FoldMarker(IDocument document, int startLine, int startColumn, int endLine, int endColumn, FoldType foldType, string foldText, bool isFolded)
        {
            this.document = document;
            checked
            {
                startLine = Math.Min(document.TotalNumberOfLines - 1, Math.Max(startLine, 0));
                ISegment lineSegment = document.GetLineSegment(startLine);
                endLine = Math.Min(document.TotalNumberOfLines - 1, Math.Max(endLine, 0));
                ISegment lineSegment2 = document.GetLineSegment(endLine);
                if (string.IsNullOrEmpty(foldText))
                {
                    foldText = "...";
                }
                FoldType = foldType;
                this.foldText = foldText;
                offset = lineSegment.Offset + Math.Min(startColumn, lineSegment.Length);
                length = lineSegment2.Offset + Math.Min(endColumn, lineSegment2.Length) - offset;
                this.isFolded = isFolded;
            }
        }

        public void ReConstruct(int startLine, int startColumn, int endLine, int endColumn, FoldType foldType, string foldText)
        {
            this.startLine = startLine;
            this.startColumn = startColumn;
            this.endLine = endLine;
            this.endColumn = endColumn;
            this.foldType = foldType;
            this.foldText = foldText;
        }

        public int CompareTo(object o)
        {
            if (!(o is FoldMarker))
            {
                throw new ArgumentException();
            }
            FoldMarker foldMarker = (FoldMarker)o;
            if (offset != foldMarker.offset)
            {
                return offset.CompareTo(foldMarker.offset);
            }
            return length.CompareTo(foldMarker.length);
        }
    }
}
